﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using SimpleSocket.Args;
using SimpleSocket.Packets;
using SimpleSocket.Protocols;

namespace SimpleSocket
{
    public class Client : Host
    {
        #region Public Properties

        /// <summary>
        /// 断开连接后是否自动重连
        /// </summary>
        public bool AutoReconnect { get; set; } = true;

        public string Ip { get; private set; }

        public int Port { get; private set; }

        public Connection SocketInfo { get; private set; }

        #endregion

        #region Private Properties

        private ConnectionInfo _connection;

        /// <summary>
        /// 消息协议
        /// </summary>
        private readonly IProtocol _msgProtocol;

        /// <summary>
        /// 是否正在连接中
        /// </summary>
        private bool _connecting = false;

        #endregion

        protected Client(IProtocol protocol) : base(protocol)
        {
            _msgProtocol = protocol;
        }

        public void Connection(string ip, int port)
        {
            Ip = ip;
            Port = port;
            Connection();
        }

        private void Connection()
        {
            if (_connecting)
                return;
            _connecting = true;
            SocketConnector.CreateSocket(new IPEndPoint(IPAddress.Parse(Ip), Port))
                .ContinueWith(ConnectCallback);
        }

        public void Send(byte[] data)
        {
            if (_msgProtocol != null)
                data = _msgProtocol.Format(data);
            _connection?.BeginSend(data);
        }

        public void BeginReceive()
        {
            _connection?.BeginReceive();
        }

        public void Dispose()
        {
            _connection?.Dispose();
        }

        private void ConnectCallback(Task<Socket> task)
        {
            _connecting = false;

            if (task.IsFaulted)
            {
                Console.WriteLine("连接失败");
                //Task.Delay(500).ContinueWith(_ => Connect());
                OnConnectionChanged($"{Ip}:{Port}", false);
                return;
            }

            var result = task.Result;
            SocketInfo = new Connection(result);
            
            if (result.RemoteEndPoint is IPEndPoint endPoint)
                SocketInfo.RemoteIp = $"{endPoint.Address}:{endPoint.Port}";
            if (result.LocalEndPoint is IPEndPoint localEndPoint)
                SocketInfo.LocalIp = $"{localEndPoint.Address}:{localEndPoint.Port}";
            
            _connection = new ConnectionInfo(SocketInfo, this);
            OnConnectionChanged($"{Ip}:{Port}", true);
        }

        internal override void OnDisconnect(string remote,Exception error)
        {
            _connection = null;
            if (AutoReconnect)
                Task.Delay(500).ContinueWith(_ => Connection());
            OnConnectionChanged(remote, false);
        }
    }
}